// glsltutorial.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "GL/glew.h"
#include "GL/glut.h"
#include "textfile.h"
#include <windows.h>
#include <olectl.h>
#include <math.h>


GLuint p,f,f2,v;

float lightPos[4] = {1,0,1,0};

GLfloat light0_ambient[4] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat light0_diffuse[4]  = {0.7f, 0.7f, 1.0f, 1.0f};
GLfloat light0_specular[4] = {0.3f, 0.3f, 1.0f, 1.0f};
GLfloat light0_position[4] = {0.0f, 0.0f, 10.0f, 1.0f};
GLfloat shininess = 128.0f;


GLuint texture[1]; //one texture atm

float angle=0.0f;

float time = 0.0f;

int BuildTexture(char* szPathName, GLuint &texid)
{
	HDC			hdcTemp;												// The DC To Hold Our Bitmap
	HBITMAP		hbmpTemp;												// Holds The Bitmap Temporarily
	IPicture	*pPicture;												// IPicture Interface
	OLECHAR		wszPath[MAX_PATH+1];									// Full Path To Picture (WCHAR)
	char*		szPath;													// Full Path To Picture
	long		lWidth;													// Width In Logical Units
	long		lHeight;												// Height In Logical Units
	long		lWidthPixels;											// Width In Pixels
	long		lHeightPixels;											// Height In Pixels
	GLint		glMaxTexDim ;											// Holds Maximum Texture Size

	szPath = szPathName;

	MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);		// Convert From ASCII To Unicode
	HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture);

	if(FAILED(hr))														// If Loading Failed
		return FALSE;													// Return False

	hdcTemp = CreateCompatibleDC(GetDC(0));								// Create The Windows Compatible Device Context
	if(!hdcTemp)														// Did Creation Fail?
	{
		pPicture->Release();											// Decrements IPicture Reference Count
		return FALSE;													// Return False (Failure)
	}

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);					// Get Maximum Texture Size Supported
	
	pPicture->get_Width(&lWidth);										// Get IPicture Width (Convert To Pixels)
	lWidthPixels	= MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540);
	pPicture->get_Height(&lHeight);										// Get IPicture Height (Convert To Pixels)
	lHeightPixels	= MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540);

	// Resize Image To Closest Power Of Two
	if (lWidthPixels <= glMaxTexDim) // Is Image Width Less Than Or Equal To Cards Limit
		lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f); 
	else  // Otherwise  Set Width To "Max Power Of Two" That The Card Can Handle
		lWidthPixels = glMaxTexDim;
 
	if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards Limit
		lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);
	else  // Otherwise  Set Height To "Max Power Of Two" That The Card Can Handle
		lHeightPixels = glMaxTexDim;
	
	//	Create A Temporary Bitmap
	BITMAPINFO	bi = {0};												// The Type Of Bitmap We Request
	DWORD		*pBits = 0;												// Pointer To The Bitmap Bits

	bi.bmiHeader.biSize			= sizeof(BITMAPINFOHEADER);				// Set Structure Size
	bi.bmiHeader.biBitCount		= 32;									// 32 Bit
	bi.bmiHeader.biWidth		= lWidthPixels;							// Power Of Two Width
	bi.bmiHeader.biHeight		= lHeightPixels;						// Make Image Top Up (Positive Y-Axis)
	bi.bmiHeader.biCompression	= BI_RGB;								// RGB Encoding
	bi.bmiHeader.biPlanes		= 1;									// 1 Bitplane

	//	Creating A Bitmap This Way Allows Us To Specify Color Depth And Gives Us Imediate Access To The Bits
	hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);
	
	if(!hbmpTemp)														// Did Creation Fail?
	{
		DeleteDC(hdcTemp);												// Delete The Device Context
		pPicture->Release();											// Decrements IPicture Reference Count
		return FALSE;													// Return False (Failure)
	}

	SelectObject(hdcTemp, hbmpTemp);									// Select Handle To Our Temp DC And Our Temp Bitmap Object

	// Render The IPicture On To The Bitmap
	pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);

	// Convert From BGR To RGB Format And Add An Alpha Value Of 255
	for(long i = 0; i < lWidthPixels * lHeightPixels; i++)				// Loop Through All Of The Pixels
	{
		BYTE* pPixel	= (BYTE*)(&pBits[i]);							// Grab The Current Pixel
		BYTE  temp		= pPixel[0];									// Store 1st Color In Temp Variable (Blue)
		pPixel[0]		= pPixel[2];									// Move Red Value To Correct Position (1st)
		pPixel[2]		= temp;											// Move Temp Value To Correct Blue Position (3rd)

		// This Will Make Any Black Pixels, Completely Transparent		(You Can Hardcode The Value If You Wish)
		if ((pPixel[0]==0) && (pPixel[1]==0) && (pPixel[2]==0))			// Is Pixel Completely Black
			pPixel[3]	=   0;											// Set The Alpha Value To 0
		else															// Otherwise
			pPixel[3]	= 255;											// Set The Alpha Value To 255
	}

	glGenTextures(1, &texid);											// Create The Texture

	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(GL_TEXTURE_2D, texid);								// Bind To The Texture ID
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);		// (Modify This For The Type Of Filtering You Want)
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);     // (Modify This For The Type Of Filtering You Want)
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits);	// (Modify This If You Want Mipmaps)

	DeleteObject(hbmpTemp);												// Delete The Object
	DeleteDC(hdcTemp);													// Delete The Device Context

	pPicture->Release();												// Decrements IPicture Reference Count

	return TRUE;		
}

void setShaders(){
	char* vertexShader="";
	char* fragmentShader="";
	//char* fragmentShader2="";

	v=glCreateShader(GL_VERTEX_SHADER);
	f=glCreateShader(GL_FRAGMENT_SHADER);
	//f2=glCreateShader(GL_FRAGMENT_SHADER);



	//vertexShader = textFileRead("..\\glsltutorial\\basic.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\basic.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\flatten.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\flatten.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\toon_part1.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\toon_part1.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\perVertexLighting.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\perVertexLighting.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\perPixelLighting.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\perPixelLighting.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\pointLightPerPixel.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\pointLightPerPixel.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\spotLightPerPixel.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\spotLightPerPixel.frag");
	//vertexShader = textFileRead("..\\glsltutorial\\basicText.vert");
	//fragmentShader = textFileRead("..\\glsltutorial\\basicText.frag");
	vertexShader = textFileRead("..\\glsltutorial\\lightAndTexture.vert");
	fragmentShader = textFileRead("..\\glsltutorial\\lightAndTexture.frag");
	//fragmentShader2 = textFileRead("..\\shaders\\toon2.frag");
	
	
	if(vertexShader == NULL  || fragmentShader == NULL /*|| fragmentShader2 == NULL*/ )
	{
		printf("vertexShader: %s \n",vertexShader);
		printf("fragmentShader: %s \n",fragmentShader);
		printf("one of the shader programs is NULL, can't compile, just fixed function pipe will be used");
		return;
	}

	const char* vv = vertexShader;
	const char* ff = fragmentShader;
	//const char* ff2 = fragmentShader2;

	glShaderSource(v,1,&vv,NULL);
	glShaderSource(f,1,&ff,NULL);
	//glShaderSource(f2,1,&ff2,NULL);

	free(vertexShader);free(fragmentShader);//free(fragmentShader2);	//delete buffers

	glCompileShader(v);						//compile shaders
	glCompileShader(f);		
	//glCompileShader(f2);	


	//gex: check compiling was ok or if an error occured, and print out compile errors:
	int vOK,fOK,f2OK;
	int length=0;
	vOK = GL_FALSE;
	fOK = GL_FALSE;
	f2OK = GL_FALSE;

	glGetShaderiv(v,GL_COMPILE_STATUS,&vOK);
	glGetShaderiv(f,GL_COMPILE_STATUS,&fOK);
	//glGetShaderiv(f2,GL_COMPILE_STATUS,&f2OK);

	if(vOK == GL_FALSE)
	{
		printf("compiling vertex shader: ERROR \n");
		glGetShaderiv(v,GL_INFO_LOG_LENGTH,&length);
		if(length>0)
		{
			char* log = (char*)malloc(length*sizeof(char));
			int len = 0;
			glGetShaderInfoLog(v,length,&len,log);
			printf("%s\n",log);
		}
	}

	if(fOK == GL_FALSE)
	{
		printf("compiling fragment shader1: ERROR \n");
		glGetShaderiv(f,GL_INFO_LOG_LENGTH,&length);
		if(length>0)
		{
			char* log = (char*)malloc(length*sizeof(char));
			int len = 0;
			glGetShaderInfoLog(f,length,&len,log);
			printf("%s\n",log);
		}
	}

	/*
	if(f2OK == GL_FALSE)
	{
		printf("compiling fragment shader2: ERROR \n");
		glGetShaderiv(f2,GL_INFO_LOG_LENGTH,&length);
		if(length>0)
		{
			char* log = (char*)malloc(length*sizeof(char));
			int len = 0;
			glGetShaderInfoLog(f2,length,&len,log);
			printf("%s\n",log);
		}
	}
	*/

	if(vOK == GL_FALSE || fOK == GL_FALSE /*|| f2OK == GL_FALSE*/)
	{
		printf("Compiling shaders went wrong, just fixed function pipe will be used\n");
		return;
	}

	//create program container
	p = glCreateProgram();
	
	//attach shaders to program container
	glAttachShader(p,v);
	glAttachShader(p,f);
	//glAttachShader(p,f2);

	/*
		detaching and deleting shaders is also possible, refer to http://www.lighthouse3d.com/opengl/glsl/index.php?oglcleanup 
		if needed
	*/

	//link program
	glLinkProgram(p);
	//use it
	glUseProgram(p);


	//set "static" uniform variables of shader
	/*GLint loc1, loc2, loc3, loc4;
	float specIntensity = 0.95f;
	float specColor[4] = {1.0f,0.0f,0.0f,1.0f};
	float threshold[2] = {0.5,0.25};
	float colors[12] = {1.0f, 0.0f, 0.0f, 1.0f,
		0.0f, 1.0f, 0.0f, 1.0f,
		0.0f,0.0f,1.0f,1.0f};

	loc1 = glGetUniformLocation(p,"specIntensity");
	glUniform1f(loc1,specIntensity);
	
	loc2 = glGetUniformLocation(p, "specColor");
	glUniform4fv(loc2,1,specColor);

	loc3 = glGetUniformLocation(p,"t");
	glUniform1fv(loc3,2,threshold);
	
	loc4=glGetUniformLocation(p,"colors");
	glUniform4fv(loc4,3,colors);*/

}

void drawCube() {

	float hd = 1.0;
	
	glBindTexture(GL_TEXTURE_2D, texture[0]);//creates texture[0] as GL_TEXTURE_2D

	//glColor3f(1,0,0);
	glBegin(GL_QUADS);
		//glTexCoord2f assings u v texture coordinates to the next vertex
		glTexCoord2f(0.0f, 0.0f);glVertex3f(-hd,-hd,-hd);
		glTexCoord2f(1.0f, 0.0f);glVertex3f(-hd,hd,-hd);
		glTexCoord2f(1.0f, 1.0f);glVertex3f(hd,hd,-hd);
		glTexCoord2f(0.0f, 1.0f);glVertex3f(hd,-hd,-hd);
	glEnd();

	//glColor3f(1,1,0);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);glVertex3f(-hd,-hd,-hd);
		glTexCoord2f(1.0f, 0.0f);glVertex3f(hd,-hd,-hd);
		glTexCoord2f(1.0f, 1.0f);glVertex3f(hd,-hd,hd);
		glTexCoord2f(0.0f, 1.0f);glVertex3f(-hd,-hd,hd);
	glEnd();

	//glColor3f(1,0,1);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);glVertex3f(-hd,-hd,-hd);
		glTexCoord2f(1.0f, 0.0f);glVertex3f(-hd,-hd,hd);
		glTexCoord2f(1.0f, 1.0f);glVertex3f(-hd,hd,hd);
		glTexCoord2f(0.0f, 1.0f);glVertex3f(-hd,hd,-hd);
	glEnd();

	//glColor3f(0,1,0);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);glVertex3f(-hd,-hd,hd);
		glTexCoord2f(1.0f, 0.0f);glVertex3f(hd,-hd,hd);
		glTexCoord2f(1.0f, 1.0f);glVertex3f(hd,hd,hd);
		glTexCoord2f(0.0f, 1.0f);glVertex3f(-hd,hd,hd);
	glEnd();

	//glColor3f(0,0,1);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);glVertex3f(-hd,hd,-hd);
		glTexCoord2f(1.0f, 0.0f);glVertex3f(-hd,hd,hd);
		glTexCoord2f(1.0f, 1.0f);glVertex3f(hd,hd,hd);
		glTexCoord2f(0.0f, 1.0f);glVertex3f(hd,hd,-hd);
	glEnd();

	
	//glColor3f(0,1,1);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);glVertex3f(hd,-hd,-hd);
		glTexCoord2f(1.0f, 0.0f);glVertex3f(hd,hd,-hd);
		glTexCoord2f(1.0f, 1.0f);glVertex3f(hd,hd,hd);
		glTexCoord2f(0.0f, 1.0f);glVertex3f(hd,-hd,hd);
	glEnd();
	

}

void changeSize(int w, int h)
{
	if(h==0)
	{
		h=1;
	}

	float ratio = 1.0*w / h;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	//set viewport
	glViewport(0,0,w,h);

	//set perspective
	gluPerspective(45,ratio,1,1000);
	glMatrixMode(GL_MODELVIEW);


}

void renderScene(void){

	//set uniform variables of shader
	
	/*GLint loc1;
	loc1=glGetUniformLocation(p,"state");
	if(((int)angle)%21==0){
		glUniform1i(loc1,0);
	}else if(((int)angle)%21==10){
		glUniform1i(loc1,1);
	}else if(((int)angle)%21==20){
		glUniform1i(loc1,2);
	}*/

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();
	gluLookAt(0.0,0.0,5.0,
		0.0,0.0,-1.0,
		0.0f,1.0f,0.0);


	//set light
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);		//lights doesn't have to be enabled cause we use fixed functionally
	

	//set material
	//GLfloat null_vector[4] = {0.0f, 0.0f, 0.0f, 1.0f};
	//GLfloat mat_ambient[4] = {0.2f, 0.2f, 0.2f, 1.0f};
	//GLfloat mat_diffuse[4] = {0.71f, 1.0f, 0.09f, 1.0f};
	//GLfloat mat_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f};
	//GLfloat mat_emission[4] = {0.71f, 1.0f, 0.09f, 1.0f};
	GLfloat null_vector[4] = {0.0f, 0.0f, 0.0f, 1.0f};
	GLfloat mat_ambient[4] = {0.2f, 0.2f, 0.2f, 1.0f};
	GLfloat mat_diffuse[4] = {1.0f, 1.0f, 0.00f, 1.0f};
	GLfloat mat_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f};
	GLfloat mat_emission[4] = {1.0f, 1.0f, 0.00f, 1.0f};
	GLfloat mat_shininess = shininess;	

	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
	glMaterialf(GL_FRONT, GL_SHININESS, mat_shininess);


	glPushMatrix();
		glRotatef(angle,0.0f,1.0f,0.0f);
		//glutSolidTeapot(1);
		drawCube();
		
		glPushMatrix();
			glTranslatef(-3.0f, 0.0f,-3.0f);
			glScalef(0.5f,0.5f,0.5f);
			glutSolidTeapot(1);
		glPopMatrix();
		
	glPopMatrix();

	glutSwapBuffers();

	angle+=0.5f;

}

void processNormalKeys(unsigned char key, int x, int y) {

	if (key == 27) 
		exit(0);
	else if(key=='+'){
		angle = angle + 2.0f;
	}
	else if(key=='-'){
		angle = angle - 2.0f;
	}
	
}



void initLights()
{
	glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
	
}

int _tmain(int argc, char** argv)
{

	//init openGL
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize (800, 600);
	glutInitWindowPosition(100, 100);
	glutCreateWindow ("GLSL Tutorial");
	
	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutKeyboardFunc(processNormalKeys);

	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0,0.0,0.0,1.0);
	//glEnable(GL_CULL_FACE);

	//loading textures
	//BuildTexture("..\\glsltutorial\\crate.bmp",texture[0]);
	BuildTexture("E:\\Uni\\14. Semester\\GLSL\\svn\\glsltutorial\\glsltutorial\\crate.bmp",texture[0]);
	//has to be adapted to use working directory :(

	//enabling textures
	glEnable(GL_TEXTURE_2D);

		
	


	//init glew
	glewInit();

	//check if everythings ready
	if(GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
	{
		printf("GLSL is enabled\n");
	}else
	{
		printf("GLSL not ready \n");
		exit(1);
	}

	if(glewIsSupported("GL_VERSION_3_2")){
		printf("OpenGL 3.2 is ready\n");	
	}else if(glewIsSupported("GL_VERSION_2_0")){
		printf("OpenGL 2.0 is ready\n");
	}else{
		printf("Neither OpenGL 3.2 or OpenGL 2.0 is ready\n");
	}
	
	setShaders();

	initLights();

	glutMainLoop();
	return 0;
}

